﻿package {
	import com.greensock.easing.Linear;
	import com.greensock.TweenLite;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.text.TextField;
	
	public class Bejeweled extends Sprite {
		
		private var SIZE		:int = 8;
		private var matrix		:Array = new Array();		
		private var sprSelect	:Sprite;
		private var previousRow	:int = -10;
		private var previousCol	:int = -10;
		private var isWait		:Boolean = false;
		private var score		:int = 0;
		
		public function Bejeweled() {
			
			var mcItem:BejeweledItem;			
			for (var i:uint = 0; i < SIZE; i++) {
				matrix[i] = new Array();
				
				for (var j:uint = 0; j < SIZE; j++) {
					
					//create item that there is not more than 2 color in row or col
					do {
						matrix[i][j] = Math.ceil(Math.random() * 7);
					} while (checkRow(i, j) > 2 || checkCol(i, j) > 2);
					
					mcItem = new BejeweledItem_FC();
					mcItem.x = j * 60;
					mcItem.y = i * 60;
					mcItem.row = i;
					mcItem.col = j;
					mcItem.name = mcItem.row + '_' + mcItem.col;
					mcItem.color = matrix[i][j];					
					
					addChild(mcItem);
				}
			}
			
			sprSelect = new Sprite();				
			sprSelect.graphics.lineStyle(2,0xff0000,1);
			sprSelect.graphics.drawRect(0,0,60,60);
			sprSelect.visible = false;
			addChild(sprSelect);
			
			stage.addEventListener(MouseEvent.CLICK, onClickHandler);
			
			addEventListener(Event.ENTER_FRAME, checkResult);
		}
		
		private function checkResult(evt:Event = null):void {
			
			if (isWait == true) {
				return;
			}
			
			var isMove:Boolean = moveItem();						
			if (isMove == false) {
				
				var isNew:Boolean = createItem();						
				if (isNew == false) {					
					
					var isReplace:Boolean = replaceItems();
					if (isReplace == false) {
						isWait = true;						
					}
				}
			}
		}
		
		private function moveItem():Boolean {
			
			var mcItem:BejeweledItem;
			var i;
			var j;
			var toY:Number;
			var isMove:Boolean = false;
			var count:int = 0;
			
			for (i = SIZE - 2; i >= 0; i--) {
				for (j = 0; j < SIZE; j++) {
					if (matrix[i][j] != -1 && matrix[i + 1][j] == -1) {
						
						matrix[i + 1][j] = matrix[i][j];
						matrix[i][j] = -1;
						
						mcItem = getChildByName(i + "_" + j) as BejeweledItem;
						toY = mcItem.y + 60;
						mcItem.name = (i + 1) + "_" + j;						
						
						TweenLite.to(mcItem, 0.1, { y: toY, ease: Linear.easeNone } );
						
						isMove = true;
					}
				}
			}	
			
			if (isMove) {
				isWait = true;
				
				TweenLite.to(this, 0.1, { alpha: 1, onComplete: function():void {
					isWait = false;
				}} );
			}
			
			return false;
		}
		
		private function createItem():Boolean {
			
			var mcItem:BejeweledItem;
			var i:int;
			var j:int;
				
			for (i = SIZE - 1; i >= 0; i--) {
				for (j = 0; j < SIZE; j++) {
					if (matrix[i][j] == -1) {
							
						//random color of item
						matrix[0][j] = Math.ceil(Math.random() * 7);
						
						//create new item for i, j position
						mcItem = new BejeweledItem_FC();
						mcItem.x = j * 60;
						mcItem.y = i * 60;
						mcItem.row = 0;
						mcItem.col = j;
						mcItem.name = mcItem.row + '_' + mcItem.col;
						mcItem.color = matrix[0][j];	
				
						mcItem.x = j * 60;
						mcItem.y = 0;
						addChild(mcItem);
						
						return true;
						
					}
				}				
			}
			
			return false;
		}
		
		private function replaceItems():Boolean {
			
			var isReplace:Boolean = false;
			var i:int;
			var j:int;
			var mcItem:BejeweledItem;
			
			for (i = SIZE - 1; i >= 0; i--) {						
				for (j = 0; j < SIZE; j++) {
					
					if (checkRow(i, j) > 2 || checkCol(i, j) > 2) {
						isReplace = true;
						
						var tr:Array = [i + "_" + j];
						var u:uint = matrix[i][j];
						var t:int;
						
						//get all items in row
						if (checkRow(i, j) > 2) {
							
							t = j;
							while (checkColor(u, i, t - 1)) {
								t--;
								tr.push(i+"_"+t);
							}
							
							t = j;
							while (checkColor(u, i, t + 1)) {
								t++;
								tr.push(i + "_" + t);
							}
						}
						
						//get all items in col
						if (checkCol(i, j) > 2) {
							t = i;
							while (checkColor(u,t-1,j)) {
								t--;
								tr.push(t + "_" + j);
							}
							
							t = i;
							while (checkColor(u, t + 1, j)) {
								t++;
								tr.push(t + "_" + j);
							}
						}
						
						//remove them
						for (i = 0; i < tr.length; i++) {
							isWait = true;
							
							mcItem = getChildByName(tr[i]) as BejeweledItem;
							
							var cd:Array = tr[i].split("_");									
							matrix[cd[0]][cd[1]] = -1;

							TweenLite.to(mcItem, 0.15, { x: mcItem.x + 30, y: mcItem.y + 30, scaleX: 0, scaleY: 0, onComplete: function(item:BejeweledItem, isEnd:Boolean):void { 
								
								if (item.parent) {
									item.parent.removeChild(item); 
								}
								
								if (isEnd) {
									isWait = false;
								}								
								
							}, onCompleteParams: [mcItem, (i == tr.length - 1)] } );
							
							score += 1;									
						}
						break;
					}
				}
				
				if (isReplace) {
					break;
				}
			}
			
			return isReplace;
		}
		
		private function onClickHandler(evt:MouseEvent):void {
			
			if (isWait) {
				
				if (mouseX < 60 * SIZE && mouseX > 0 && mouseY < 60 * SIZE && mouseY > 0) {
					
					var sr:int = Math.floor(mouseY / 60);
					var sc:int = Math.floor(mouseX / 60);
					
					//if the selected item is not nearest row or nearest col then previous is new one
					if (!(((sr == previousRow + 1 || sr == previousRow - 1) && sc == previousCol) || ((sc == previousCol + 1 || sc == previousCol - 1) && sr == previousRow))) {
						previousRow = sr;
						previousCol = sc;
						
						sprSelect.x = 60 * previousCol;
						sprSelect.y = 60 * previousRow;
						sprSelect.visible = true;
					} 
										
					//if the selected item is nearest row or nearest col then check the total color of row or col
					else {
						swapItem(previousRow, previousCol, sr, sc);
						
						//if total color of row or col is more than 2, get score
						if (checkRow(previousRow, previousCol) > 2 || checkCol(previousRow, previousCol) > 2 || checkRow(sr, sc) > 2 || checkCol(sr, sc) > 2) {
							
							isWait = true;
							
							//previouse item
							var mcItem:BejeweledItem = getChildByName(previousRow + "_" + previousCol) as BejeweledItem;
							mcItem.name = "_temp_";							
							TweenLite.to(mcItem, 0.3, { x: sc * 60, y: sr * 60} );
							
							//new item
							mcItem = getChildByName(sr + "_" + sc) as BejeweledItem;
							mcItem.name = previousRow + "_" + previousCol;
							TweenLite.to(mcItem, 0.3, { x: previousCol * 60, y: previousRow * 60, onComplete: function():void { 
								isWait = false }
							} );
							
							getChildByName("_temp_").name = sr + "_" + sc;							
						} 
						
						//if total color of row or col is less than 3
						else {							
							isWait = true;
							
							var mcItem1:BejeweledItem = getChildByName(previousRow + "_" + previousCol) as BejeweledItem;
							var mcItem2:BejeweledItem = getChildByName(sr + "_" + sc) as BejeweledItem;
							
							TweenLite.to(mcItem1, 0.3, { x: mcItem2.x, y: mcItem2.y } );
							TweenLite.to(mcItem2, 0.3, { x: mcItem1.x, y: mcItem1.y, onComplete: function():void { 
								
								TweenLite.to(mcItem1, 0.3, { x: mcItem2.x, y: mcItem2.y } );
								TweenLite.to(mcItem2, 0.3, { x: mcItem1.x, y: mcItem1.y, onComplete: function():void {
									isWait = false;									
								} } );
								
							}, onCompleteParams: [mcItem1, mcItem2]} );
													
							swapItem(previousRow, previousCol, sr, sc);
						}
						
						previousRow = -10;
						previousCol = -10;						
						sprSelect.visible = false;
					}
				}	
			}		
		}

		private function swapItem(row1:int, col1:int, row2:int, col2:int):void {
			var temp:uint = matrix[row1][col1];
			matrix[row1][col1] = matrix[row2][col2];
			matrix[row2][col2] = temp;
		}
		
		private function checkRow(row:int, col:int):int {			
			var color:uint = matrix[row][col];			
			var total:int = 1;
			var i:int = col;
			
			//check left 0 0 0 1 . . . 
			while (checkColor(color, row, i - 1)) {
				i--;
				total++;
			}
			
			//check right . . . 1 0 0 0
			i = col;
			while (checkColor(color, row, i + 1)) {
				i++;
				total++;
			}
			return total;
		}
		
		
		private function checkCol(row:int, col:int):int {			
			var color:int = matrix[row][col];
			var total:int = 1;
			var j:int = row;
			
			//check up 
			// 0 
			// 0 
			// 0 
			// 1 
			// . 
			// . 
			// . 
			while (checkColor(color, j - 1, col)) {
				j--;
				total++;
			}
			
			//check down
			// . 
			// . 
			// . 
			// 1 
			// 0 
			// 0 
			// 0
			j = row;
			while (checkColor(color, j + 1, col)) {
				j++;
				total++;
			}
			
			return total;
		}
		
		private function checkColor(color:uint, row:int, col:int):Boolean {
			if (matrix[row] == null) {
				return false;
			}
			
			if (matrix[row][col] == null) {
				return false;
			}
			
			return (color == matrix[row][col]);
		}
	}
}